#include "cydevice.h"

CyDevice::CyDevice()
	: device(nullptr),
	isReceiving(false)
{
}

CyDevice::~CyDevice()
{
}

bool CyDevice::open(HANDLE hnd, int width, int height)
{
	if (device != nullptr) {
		return false;
	}
	device = new CCyUSBDevice(hnd);
	if (!device->Open(0)) {
		delete device;
		device = nullptr;
		return false;
	}

	dataInEndPoint = device->BulkInEndPt;
	controlEndPoint = device->ControlEndPt;

	sendControlCode(0xb2);
	sendControlCode(0xb4);

	dataInEndPoint->SetXferSize(1024);

	this->width = width;
	this->height = height;

	setExposure(1);
	setTrigger(TriggerType::Internal);
}

void CyDevice::close()
{
	if (device != nullptr) {
		clear();
		delete device;
		device = nullptr;
	}
}

void CyDevice::enableReceive(bool enable)
{
	mutex.lockForWrite();
	isReceiving = enable;
	mutex.unlock();
}

bool CyDevice::receiving()
{
	mutex.lockForRead();
	bool receving = isReceiving;
	mutex.unlock();
	return receving;
}

void CyDevice::clear()
{
	for (auto& buffer : receiveQueue) {
		delete[] buffer.data;
	}
	receiveQueue.clear();
}

bool CyDevice::empty()
{
	return receiveQueue.empty();
}

ReceiveData CyDevice::read()
{
	return receiveQueue.dequeue();
}

void CyDevice::start()
{
	enableReceive(true);
	receive();
}

void CyDevice::stop()
{
	enableReceive(false);
}

void CyDevice::receive()
{
	while (true) {
		mutex.lockForRead();
		if (!isReceiving) {
			mutex.unlock();
			break;
		}

		sendControlCode(triggerType == TriggerType::Internal ? 0xb5 : 0xb9);

		LONG recvSize = width * height * 2;
		uchar* recvBuffer = new uchar[recvSize];

		dataInEndPoint->XferData(recvBuffer, recvSize);
		receiveQueue.enqueue(ReceiveData(recvBuffer, height, width));

		emit received();
	}
}

void CyDevice::setExposure(int exposure)
{
	sendRequestCode(0xb3, { uchar(exposure), uchar(exposure >> 8),
		uchar(exposure >> 16), uchar(exposure >> 24)});
}

void CyDevice::setTrigger(TriggerType trigger)
{
	mutex.lockForWrite();
	triggerType = trigger;
	mutex.unlock();
}

bool CyDevice::sendControlCode(int code)
{
	controlEndPoint->Target = TGT_DEVICE;
	controlEndPoint->ReqType = REQ_VENDOR;
	controlEndPoint->Direction = DIR_TO_DEVICE;
	controlEndPoint->Value = 0;
	controlEndPoint->Index = 0;
	controlEndPoint->ReqCode = code;

	UCHAR buf = 0;
	LONG len = 0;
	return controlEndPoint->XferData(&buf, len);
}

bool CyDevice::sendRequestCode(int code, QVector<uchar> buf)
{
	controlEndPoint->Target = TGT_DEVICE;
	controlEndPoint->ReqType = REQ_VENDOR;
	controlEndPoint->Direction = DIR_TO_DEVICE;
	controlEndPoint->Value = 0;
	controlEndPoint->Index = 0;
	controlEndPoint->ReqCode = code;

	LONG len = buf.size();
	return controlEndPoint->XferData(buf.data(), len);
}

